
#include "DevicesLed.h"


LedInfoType st_typeLedInfo;

/* 周期调用计数 */
static uint32_t st_uiLedTickCnt = 0;
/* 呼吸档位 */
static int16_t st_sGrade = 0;


void vLedInit(void)
{
    ledc_channel_config_t ledc_channel = {0};

    /* Prepare and then apply the LEDC PWM channel configuration */
    ledc_channel.gpio_num       = LED_SYSTEM_PIN;
    ledc_channel.speed_mode     = LEDC_LOW_SPEED_MODE;
    ledc_channel.channel        = LED_SYSTEM_CHANNEL;
    ledc_channel.intr_type      = LEDC_INTR_DISABLE;
    ledc_channel.timer_sel      = LEDC_TIMER_0;
    ledc_channel.duty           = 0;
    ledc_channel.hpoint         = 0;
    ledc_channel.flags.output_invert    = 1;
    ledc_channel_config(&ledc_channel);

    /* 记录通道号 */
    st_typeLedInfo.system.channel = LED_SYSTEM_CHANNEL;

    vLedBreathe(LED_CHANNEL_ALL);
}

void vLedOpen(uint32_t uiChannel)
{
    if(uiChannel & LED_CHANNEL_SYSTEM)
    {
        /* Set duty to 100% */
        ledc_set_duty(LEDC_LOW_SPEED_MODE, LED_SYSTEM_CHANNEL, (1 << 12));
        /* Update duty to apply the new value */
        ledc_update_duty(LEDC_LOW_SPEED_MODE, LED_SYSTEM_CHANNEL);
    }
}

void vLedClose(uint32_t uiChannel)
{
    if(uiChannel & LED_CHANNEL_SYSTEM)
    {
        /* Set duty to 0% */
        ledc_set_duty(LEDC_LOW_SPEED_MODE, LED_SYSTEM_CHANNEL, 0);
        /* Update duty to apply the new value */
        ledc_update_duty(LEDC_LOW_SPEED_MODE, LED_SYSTEM_CHANNEL);
    }
}

void vLedRevesal(uint32_t uiChannel)
{
    if(uiChannel & LED_CHANNEL_SYSTEM)
    {
        /* Set duty */
        ledc_set_duty(LEDC_LOW_SPEED_MODE, LED_SYSTEM_CHANNEL, ledc_get_duty(LEDC_LOW_SPEED_MODE, LED_SYSTEM_CHANNEL) ? 0 : (1 << 12));
        /* Update duty to apply the new value */
        ledc_update_duty(LEDC_LOW_SPEED_MODE, LED_SYSTEM_CHANNEL);
    }
}

static void vLedDutySet(LedType *ptypeLed, float fDuty)
{
    fDuty = (fDuty < 0.0f) ? 0.0f : ((fDuty > 1.0f) ? 1.0f : fDuty);

    /* Set duty */
    ledc_ll_set_duty_int_part(&LEDC, LEDC_LOW_SPEED_MODE, ptypeLed->channel, (1 << 12) * fDuty);
    /* Update duty to apply the new value */
    ledc_ll_set_duty_start(&LEDC, LEDC_LOW_SPEED_MODE, ptypeLed->channel, true);
    ledc_ll_ls_channel_update(&LEDC, LEDC_LOW_SPEED_MODE, ptypeLed->channel);
}

/*
 * Return:      void
 * Parameters:  usChannel: LED通道; usChannel: 状态; usChannel: 闪烁次数/或者占空比;
 * Description: 设置LED状态
 */
void vLedStatusSet(LedChannelEnum usChannel, LedStateEnum enumStatus, uint8_t ucCntOrDuty)
{
    if(usChannel & LED_CHANNEL_SYSTEM)
    {
        st_typeLedInfo.system.state     = enumStatus;
        st_typeLedInfo.system.cnt       = ucCntOrDuty * 2 + 1;
        st_typeLedInfo.system.duty      = ucCntOrDuty % 101;
    }
}

/*
 * Return:      void
 * Parameters:  *ptypeLed: LED信息结构体
 * Description: LED状态刷新
 */
static void vLedStateMachine(LedType *ptypeLed)
{
    uint32_t uiPeriod;

    switch(ptypeLed->state)
    {
        /* 关闭 */
        case LED_DISABLE:
            vLedDutySet(ptypeLed, 0.0f);
            ptypeLed->state = LED_IDLE;
            break;

        /* 常亮 */
        case LED_ENABLE:
            vLedDutySet(ptypeLed, LED_HIGH_DUTY);
            ptypeLed->state = LED_IDLE;
            break;

        /* 常亮 - 低亮 */
        case LED_ENABLE_LOW:
            vLedDutySet(ptypeLed, LED_LOW_DUTY);
            ptypeLed->state = LED_IDLE;
            break;

        /* 呼吸 */
        case LED_BREATHE:
            /* 符合人类生理的呼吸曲线：y = x * x */
            vLedDutySet(ptypeLed, st_sGrade < 0 ? 0.0f : st_sGrade * st_sGrade * (1.0f / 125.0f / 125.0f));
            break;

        /* 固定占空比 */
        case LED_DUTY:
            vLedDutySet(ptypeLed, ptypeLed->duty * 0.01f);
            ptypeLed->state = LED_IDLE;
            break;

        /* 快速频率---持续闪烁、闪烁后常亮、闪烁后关闭 */
        case LED_FLASH_FAST:
        case LED_FLASH_FAST_ENABLE_CNT:
        case LED_FLASH_FAST_DISABLE_CNT:
        /* 低速频率---持续闪烁、闪烁后常亮、闪烁后关闭 */
        case LED_FLASH_SLOW:
        case LED_FLASH_SLOW_ENABLE_CNT:
        case LED_FLASH_SLOW_DISABLE_CNT:
            uiPeriod = (ptypeLed->state >= LED_FLASH_SLOW) ? (LED_FLASH_SLOW_PERIOD / 2) : (LED_FLASH_FAST_PERIOD / 2);

            if((st_uiLedTickCnt % uiPeriod) == 0)
            {
                vLedDutySet(ptypeLed, ((st_uiLedTickCnt / uiPeriod) & 1) ? 1.0f : 0.0f);

                if((ptypeLed->state != LED_FLASH_FAST) && (ptypeLed->state != LED_FLASH_SLOW) && ((ptypeLed->cnt--) <= 0))
                {
                    ptypeLed->state = ((ptypeLed->state == LED_FLASH_FAST_ENABLE_CNT) || (ptypeLed->state == LED_FLASH_SLOW_ENABLE_CNT)) ? LED_ENABLE : LED_DISABLE;
                }
            }
            break;

        default : break;
    }
}

/*
 * Return:      void
 * Parameters:  void
 * Description: LED心跳计数 + 呼吸状态机
 */
static void vLedIncTick(void)
{
    /* 呼吸方向 */
    static int8_t st_cDirection = 1;

    /* 所有LED使用同一个状态机变量，以方便实现一致的闪烁步调 */
    st_uiLedTickCnt += LED_EXECUTION_PERIOD;

    /* 更新呼吸模式占空比 */
    {
        st_sGrade += st_cDirection;
        /* 之所以把档位设置的比 0 小一点，是为了在呼吸状态时，LED能够彻底熄灭一段时间 */
        st_cDirection = (st_sGrade >= 125) ? -1 : ((st_sGrade <= -25) ? 1 : st_cDirection);
    }
}

/* 20ms调用一次 */
void vLedMachine(void)
{
    LedType *ptypeLed = (LedType *)(&st_typeLedInfo);
    int8_t i;

    /* 更新1次计数值 */
    vLedIncTick();

    /* 扫描所有的led状态 */
    for(i = 0; i < (sizeof(LedInfoType) / sizeof(LedType)); ++i)
    {
        vLedStateMachine(ptypeLed++);
    }
}

LedInfoType *ptypeLedGetInfo(void)
{
    return &st_typeLedInfo;
}
